﻿using System;
using System.Linq;
using Skynet.Services.Capture;
using System.ComponentModel.Composition.Hosting;
using System.Collections.Generic;
using Skynet.Contracts;
using System.IO;
using Skynet.Services.Extension;
using System.Reflection;
using System.ComponentModel.Composition;
using CaptureFrameBase = Skynet.Contracts.Capture.FrameBase;
using CaptureAudioFrame = Skynet.Contracts.Capture.AudioFrame;
using CaptureVideoFrame = Skynet.Contracts.Capture.VideoFrame;
namespace Skynet.Services
{
    /// <summary>
    /// IExtensionService
    /// </summary>
    public interface IExtensionService : IDisposable
    {
        Action<Action<ICaptureServiceCallback>, Predicate<Subscriber>> Broadcast { get; }
        void Initialize();
        void Sync(IEnumerable<Subscriber> subscribers, bool forceRebuild);
    }

    /// <summary>
    /// ExtensionService
    /// </summary>
    public class ExtensionService : IExtensionService, ITransport
    {
        private static ExtensionFactory _factory;

        public ExtensionService(Action<Action<ICaptureServiceCallback>, Predicate<Subscriber>> callback)
        {
            Broadcast = callback;
            Container = GetContainer();
        }
        public void Dispose()
        {
            Container.Dispose(); Container = null;
        }

        public Action<Action<ICaptureServiceCallback>, Predicate<Subscriber>> Broadcast { get; private set; }

        public void Initialize()
        {
            _factory = Container.GetExportedValue<ExtensionFactory>();
            _factory.InitializeCaptureDevices(this);
            _factory.SyncEnabledCaptureDevices();
        }

        public void Sync(IEnumerable<Subscriber> subscribers, bool forceRebuild)
        {
            var requestedCaptureTypes = (subscribers.Any(x => x.Subscription.RequestAudio) ? CaptureTypes.Audio : 0)
                | (subscribers.Any(x => x.Subscription.RequestVideo) ? CaptureTypes.Video : 0);
            _factory.Sync(this, requestedCaptureTypes);
        }

        #region Transport

        public void Transmit(CaptureFrameBase frame)
        {
            CaptureAudioFrame captureAudioFrame;
            CaptureVideoFrame captureVideoFrame;
            if ((captureAudioFrame = (frame as CaptureAudioFrame)) != null)
            {
                Broadcast(x => x.OnFrame(new AudioFrame
                {
                    Data = captureAudioFrame.Data,
                }), x => x.Subscription.RequestAudio);
            }
            else if ((captureVideoFrame = (frame as CaptureVideoFrame)) != null)
                Broadcast(x => x.OnFrame(new VideoFrame
                {
                    Data = captureVideoFrame.Data,
                }), x => x.Subscription.RequestVideo);
            else
                throw new ArgumentOutOfRangeException("frame", "Unknown Type");
        }

        #endregion

        #region Container

        public static CompositionContainer Container { get; private set; }

        private static CompositionContainer GetContainer()
        {
            var pluginPath = Environment.CurrentDirectory + "\\Plugins\\";
            if (!Directory.Exists(pluginPath))
                Directory.CreateDirectory(pluginPath);
            var directoryCatalog = new DirectoryCatalog(pluginPath);
            var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            return new CompositionContainer(new AggregateCatalog(assemblyCatalog, directoryCatalog));
        }

        /// <summary>
        /// ServiceLocator
        /// </summary>
        [Export(typeof(IServiceLocator))]
        public class ServiceLocator : IServiceLocator
        {
            public T Resolve<T>() { return Container.GetExportedValueOrDefault<T>(); }
        }

        #endregion
    }
}
